package com.jdk.learn.nio._03;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * Created by ricky
 * on 2018/2/7.
 * <p>
 * 一、通道(Channel):用于源节点和目标节点的链接。在JavaNio中负责缓冲区中的数据传输。
 * channel不存储数据，因此需要配合缓冲区进行传输。
 * 二、通道的主要实现类
 * java.nio.channels.Channel 接口
 * |--FileChannel  本地文件操作
 * |--SocketChannel TCP网络客户端
 * |--ServerSocketChannel TCP服务端
 * |--DatagramChannel UDP方式的网络通道
 * <p>
 * 三、获取通道
 * 1.Java针对支持通道的类提供了getChannel()方法
 * 本地IO
 * FileInputStream/FileOutputStream
 * RandomAccessFile
 * <p>
 * 网络IO
 * Socket/ServerSocket/DatagramSocket
 * 2.在jdk1.7中针对各个通道提供了静态open方法
 * 3. --------的Files工具类的newByteChannel()
 */
public class TestChannel {

    /**
     *
     分散(Scatter)和聚集(Gather)
      分散读取（Scattering Reads）是指从 Channel 中读取的数据“分
     散”到多个 Buffer 中。

      聚集写入（Gathering Writes）是指将多个 Buffer 中的数据“聚集”
     到 Channel。
     */

    @Test
    public void TestReadWrite() throws IOException {

        RandomAccessFile raf=new RandomAccessFile("README.md","rw");


        ByteBuffer buf1=ByteBuffer.allocate(10);
        ByteBuffer buf2=ByteBuffer.allocate(1000);
        ByteBuffer[] bs={buf1,buf2};
        FileChannel channel = raf.getChannel();
        channel.read(bs);
        for (ByteBuffer b : bs) {
            b.flip();
        }
        System.out.print(new String(bs[0].array(),0,bs[0].limit()));
        //System.out.println("--------------");
        System.out.print(new String(bs[1].array(),0,bs[1].limit()));
        System.out.println("\n--------------");
        RandomAccessFile rafwrite=new RandomAccessFile("READMEcopy.md","rw");
        FileChannel channel1 = rafwrite.getChannel();
        channel1.write(bs);
    }

    /**
     * 直接缓冲区的方式  通道间相互传输
     */
    @Test
    public void ChannelDirDemo2() {
        FileChannel read = null;
        FileChannel write = null;
        try {
            read = FileChannel.open(Paths.get("ricky.png"), StandardOpenOption.READ);
            write = FileChannel.open(Paths.get("rickydir2.png"), StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);
//            write.transferFrom(read,0,read.size());
            read.transferTo(0,write.size(),write);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
          close(read,write);
        }
    }
    /**
     * 直接缓冲区的方式
     */
    @Test
    public void ChannelDirDemo() {
        FileChannel read = null;
        FileChannel write = null;
        try {
            read = FileChannel.open(Paths.get("ricky.png"), StandardOpenOption.READ);
            write = FileChannel.open(Paths.get("rickydir.png"), StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE);

            MappedByteBuffer mappedReadByteBuffer = read.map(FileChannel.MapMode.READ_ONLY, 0, read.size());
            MappedByteBuffer mappedWriteByteBuffer = write.map(FileChannel.MapMode.READ_WRITE, 0, read.size());

            byte[] buf=new byte[mappedReadByteBuffer.limit()];
            mappedReadByteBuffer.get(buf);
            mappedWriteByteBuffer.put(buf);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(read,write);
        }
    }

    /**
     * 非直接缓冲区的方式
     */
    public void ChannelDemo() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel inchannel = null;
        FileChannel outchannel = null;
        try {
            fis = new FileInputStream("ricky.png");
            fos = new FileOutputStream("rickycopy.png");

            //获取通道
            inchannel = fis.getChannel();
            outchannel = fos.getChannel();

            ByteBuffer buffer = ByteBuffer.allocate(1024);

            while (inchannel.read(buffer) != -1) {
                buffer.flip();
                //写入数据
                outchannel.write(buffer);
                buffer.clear();
            }
        } catch (Exception e) {

        } finally {
            try {
                outchannel.close();
                inchannel.close();
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    public void close(FileChannel read,FileChannel write){
        try {
            if (read != null) {
                read.close();
            }
            if (write != null) {
                write.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
